/*
	2	+	
	3	+	   Copyright [2008] [Trevor Hogan]
	4	+	
	5	+	   Licensed under the Apache License, Version 2.0 (the "License");
	6	+	   you may not use this file except in compliance with the License.
	7	+	   You may obtain a copy of the License at
	8	+	
	9	+	       http://www.apache.org/licenses/LICENSE-2.0
	10	+	
	11	+	   Unless required by applicable law or agreed to in writing, software
	12	+	   distributed under the License is distributed on an "AS IS" BASIS,
	13	+	   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	14	+	   See the License for the specific language governing permissions and
	15	+	   limitations under the License.
	16	+	
	17	+	   CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
	18	+	
	19	+	*/
	20	+	
	21	+	#include "ghost.h"
	22	+	#include "util.h"
	23	+	#include "crc32.h"
	24	+	#include "sha1.h"
	25	+	#include "csvparser.h"
	26	+	#include "config.h"
	27	+	#include "language.h"
	28	+	#include "socket.h"
	29	+	#include "ghostdb.h"
	30	+	#include "ghostdbsqlite.h"
	31	+	#include "ghostdbmysql.h"
	32	+	#include "bnet.h"
	33	+	#include "map.h"
	34	+	#include "packed.h"
	35	+	#include "savegame.h"
	36	+	#include "gameplayer.h"
	37	+	#include "gameprotocol.h"
	38	+	#include "gpsprotocol.h"
	39	+	#include "game_base.h"
	40	+	#include "game.h"
	41	+	#include "game_admin.h"
	42	+	
	43	+	#include <signal.h>
	44	+	#include <stdlib.h>
	45	+	
	46	+	#ifdef WIN32
	47	+	 #include <ws2tcpip.h>		// for WSAIoctl
	48	+	#endif
	49	+	
	50	+	#define __STORMLIB_SELF__
	51	+	#include <stormlib/StormLib.h>
	52	+	
	53	+	/*
	54	+	
	55	+	#include "ghost.h"
	56	+	#include "util.h"
	57	+	#include "crc32.h"
	58	+	#include "sha1.h"
	59	+	#include "csvparser.h"
	60	+	#include "config.h"
	61	+	#include "language.h"
	62	+	#include "socket.h"
	63	+	#include "commandpacket.h"
	64	+	#include "ghostdb.h"
	65	+	#include "ghostdbsqlite.h"
	66	+	#include "ghostdbmysql.h"
	67	+	#include "bncsutilinterface.h"
	68	+	#include "warden.h"
	69	+	#include "bnlsprotocol.h"
	70	+	#include "bnlsclient.h"
	71	+	#include "bnetprotocol.h"
	72	+	#include "bnet.h"
	73	+	#include "map.h"
	74	+	#include "packed.h"
	75	+	#include "savegame.h"
	76	+	#include "replay.h"
	77	+	#include "gameslot.h"
	78	+	#include "gameplayer.h"
	79	+	#include "gameprotocol.h"
	80	+	#include "gpsprotocol.h"
	81	+	#include "game_base.h"
	82	+	#include "game.h"
	83	+	#include "game_admin.h"
	84	+	#include "stats.h"
	85	+	#include "statsdota.h"
	86	+	#include "sqlite3.h"
	87	+	
	88	+	*/
	89	+	
	90	+	#ifdef WIN32
	91	+	 #include <windows.h>
	92	+	 #include <winsock.h>
	93	+	#endif
	94	+	
	95	+	#include <time.h>
	96	+	
	97	+	#ifndef WIN32
	98	+	 #include <sys/time.h>
	99	+	#endif
	100	+	
	101	+	#ifdef __APPLE__
	102	+	 #include <mach/mach_time.h>
	103	+	#endif
	104	+	
	105	+	string gCFGFile;
	106	+	string gLogFile;
	107	+	uint32_t gLogMethod;
	108	+	ofstream *gLog = NULL;
	109	+	CGHost *gGHost = NULL;
	110	+	
	111	+	uint32_t GetTime( )
	112	+	{
	113	+		return GetTicks( ) / 1000;
	114	+	}
	115	+	
	116	+	uint32_t GetTicks( )
	117	+	{
	118	+	#ifdef WIN32
	119	+		// don't use GetTickCount anymore because it's not accurate enough (~16ms resolution)
	120	+		// don't use QueryPerformanceCounter anymore because it isn't guaranteed to be strictly increasing on some systems and thus requires "smoothing" code
	121	+		// use timeGetTime instead, which typically has a high resolution (5ms or more) but we request a lower resolution on startup
	122	+	
	123	+		return timeGetTime( );
	124	+	#elif __APPLE__
	125	+		uint64_t current = mach_absolute_time( );
	126	+		static mach_timebase_info_data_t info = { 0, 0 };
	127	+		// get timebase info
	128	+		if( info.denom == 0 )
	129	+			mach_timebase_info( &info );
	130	+		uint64_t elapsednano = current * ( info.numer / info.denom );
	131	+		// convert ns to ms
	132	+		return elapsednano / 1e6;
	133	+	#else
	134	+		uint32_t ticks;
	135	+		struct timespec t;
	136	+		clock_gettime( CLOCK_MONOTONIC, &t );
	137	+		ticks = t.tv_sec * 1000;
	138	+		ticks += t.tv_nsec / 1000000;
	139	+		return ticks;
	140	+	#endif
	141	+	}
	142	+	
	143	+	void SignalCatcher2( int s )
	144	+	{
	145	+		CONSOLE_Print( "[!!!] caught signal " + UTIL_ToString( s ) + ", exiting NOW" );
	146	+	
	147	+		if( gGHost )
	148	+		{
	149	+			if( gGHost->m_Exiting )
	150	+				exit( 1 );
	151	+			else
	152	+				gGHost->m_Exiting = true;
	153	+		}
	154	+		else
	155	+			exit( 1 );
	156	+	}
	157	+	
	158	+	void SignalCatcher( int s )
	159	+	{
	160	+		// signal( SIGABRT, SignalCatcher2 );
	161	+		signal( SIGINT, SignalCatcher2 );
	162	+	
	163	+		CONSOLE_Print( "[!!!] caught signal " + UTIL_ToString( s ) + ", exiting nicely" );
	164	+	
	165	+		if( gGHost )
	166	+			gGHost->m_ExitingNice = true;
	167	+		else
	168	+			exit( 1 );
	169	+	}
	170	+	
	171	+	void CONSOLE_Print( string message )
	172	+	{
	173	+		cout << message << endl;
	174	+	
	175	+		// logging
	176	+	
	177	+		if( !gLogFile.empty( ) )
	178	+		{
	179	+			if( gLogMethod == 1 )
	180	+			{
	181	+				ofstream Log;
	182	+				Log.open( gLogFile.c_str( ), ios :: app );
	183	+	
	184	+				if( !Log.fail( ) )
	185	+				{
	186	+					time_t Now = time( NULL );
	187	+					string Time = asctime( localtime( &Now ) );
	188	+	
	189	+					// erase the newline
	190	+	
	191	+					Time.erase( Time.size( ) - 1 );
	192	+					Log << "[" << Time << "] " << message << endl;
	193	+					Log.close( );
	194	+				}
	195	+			}
	196	+			else if( gLogMethod == 2 )
	197	+			{
	198	+				if( gLog && !gLog->fail( ) )
	199	+				{
	200	+					time_t Now = time( NULL );
	201	+					string Time = asctime( localtime( &Now ) );
	202	+	
	203	+					// erase the newline
	204	+	
	205	+					Time.erase( Time.size( ) - 1 );
	206	+					*gLog << "[" << Time << "] " << message << endl;
	207	+					gLog->flush( );
	208	+				}
	209	+			}
	210	+		}
	211	+	}
	212	+	
	213	+	void DEBUG_Print( string message )
	214	+	{
	215	+		cout << message << endl;
	216	+	}
	217	+	
	218	+	void DEBUG_Print( BYTEARRAY b )
	219	+	{
	220	+		cout << "{ ";
	221	+	
	222	+	        for( unsigned int i = 0; i < b.size( ); ++i )
	223	+			cout << hex << (int)b[i] << " ";
	224	+	
	225	+		cout << "}" << endl;
	226	+	}
	227	+	
	228	+	//
	229	+	// main
	230	+	//
	231	+	
	232	+	int main( int argc, char **argv )
	233	+	{
	234	+		srand( time( NULL ) );
	235	+	
	236	+		gCFGFile = "ghost.cfg";
	237	+	
	238	+		if( argc > 1 && argv[1] )
	239	+			gCFGFile = argv[1];
	240	+	
	241	+		// read config file
	242	+	
	243	+		CConfig CFG;
	244	+		CFG.Read( "default.cfg" );
	245	+		CFG.Read( gCFGFile );
	246	+		gLogFile = CFG.GetString( "bot_log", string( ) );
	247	+		gLogMethod = CFG.GetInt( "bot_logmethod", 1 );
	248	+	
	249	+		if( !gLogFile.empty( ) )
	250	+		{
	251	+			if( gLogMethod == 1 )
	252	+			{
	253	+				// log method 1: open, append, and close the log for every message
	254	+				// this works well on Linux but poorly on Windows, particularly as the log file grows in size
	255	+				// the log file can be edited/moved/deleted while GHost++ is running
	256	+			}
	257	+			else if( gLogMethod == 2 )
	258	+			{
	259	+				// log method 2: open the log on startup, flush the log for every message, close the log on shutdown
	260	+				// the log file CANNOT be edited/moved/deleted while GHost++ is running
	261	+	
	262	+				gLog = new ofstream( );
	263	+				gLog->open( gLogFile.c_str( ), ios :: app );
	264	+			}
	265	+		}
	266	+	
	267	+		CONSOLE_Print( "[GHOST] starting up" );
	268	+	
	269	+		if( !gLogFile.empty( ) )
	270	+		{
	271	+			if( gLogMethod == 1 )
	272	+				CONSOLE_Print( "[GHOST] using log method 1, logging is enabled and [" + gLogFile + "] will not be locked" );
	273	+			else if( gLogMethod == 2 )
	274	+			{
	275	+				if( gLog->fail( ) )
	276	+					CONSOLE_Print( "[GHOST] using log method 2 but unable to open [" + gLogFile + "] for appending, logging is disabled" );
	277	+				else
	278	+					CONSOLE_Print( "[GHOST] using log method 2, logging is enabled and [" + gLogFile + "] is now locked" );
	279	+			}
	280	+		}
	281	+		else
	282	+			CONSOLE_Print( "[GHOST] no log file specified, logging is disabled" );
	283	+	
	284	+		// catch SIGABRT and SIGINT
	285	+	
	286	+		// signal( SIGABRT, SignalCatcher );
	287	+		signal( SIGINT, SignalCatcher );
	288	+	
	289	+	#ifndef WIN32
	290	+		// disable SIGPIPE since some systems like OS X don't define MSG_NOSIGNAL
	291	+	
	292	+		signal( SIGPIPE, SIG_IGN );
	293	+	#endif
	294	+	
	295	+	#ifdef WIN32
	296	+		// initialize timer resolution
	297	+		// attempt to set the resolution as low as possible from 1ms to 5ms
	298	+	
	299	+		unsigned int TimerResolution = 0;
	300	+	
	301	+	        for( unsigned int i = 1; i <= 5; ++i )
	302	+		{
	303	+			if( timeBeginPeriod( i ) == TIMERR_NOERROR )
	304	+			{
	305	+				TimerResolution = i;
	306	+				break;
	307	+			}
	308	+			else if( i < 5 )
	309	+				CONSOLE_Print( "[GHOST] error setting Windows timer resolution to " + UTIL_ToString( i ) + " milliseconds, trying a higher resolution" );
	310	+			else
	311	+			{
	312	+				CONSOLE_Print( "[GHOST] error setting Windows timer resolution" );
	313	+				return 1;
	314	+			}
	315	+		}
	316	+	
	317	+		CONSOLE_Print( "[GHOST] using Windows timer with resolution " + UTIL_ToString( TimerResolution ) + " milliseconds" );
	318	+	#elif __APPLE__
	319	+		// not sure how to get the resolution
	320	+	#else
	321	+		// print the timer resolution
	322	+	
	323	+		struct timespec Resolution;
	324	+	
	325	+		if( clock_getres( CLOCK_MONOTONIC, &Resolution ) == -1 )
	326	+			CONSOLE_Print( "[GHOST] error getting monotonic timer resolution" );
	327	+		else
	328	+			CONSOLE_Print( "[GHOST] using monotonic timer with resolution " + UTIL_ToString( (double)( Resolution.tv_nsec / 1000 ), 2 ) + " microseconds" );
	329	+	#endif
	330	+	
	331	+	#ifdef WIN32
	332	+		// initialize winsock
	333	+	
	334	+		CONSOLE_Print( "[GHOST] starting winsock" );
	335	+		WSADATA wsadata;
	336	+	
	337	+		if( WSAStartup( MAKEWORD( 2, 2 ), &wsadata ) != 0 )
	338	+		{
	339	+			CONSOLE_Print( "[GHOST] error starting winsock" );
	340	+			return 1;
	341	+		}
	342	+	
	343	+		// increase process priority
	344	+	
	345	+		CONSOLE_Print( "[GHOST] setting process priority to \"above normal\"" );
	346	+		SetPriorityClass( GetCurrentProcess( ), ABOVE_NORMAL_PRIORITY_CLASS );
	347	+	#endif
	348	+	
	349	+		// initialize ghost
	350	+	
	351	+		gGHost = new CGHost( &CFG );
	352	+	
	353	+		while( 1 )
	354	+		{
	355	+			// block for 50ms on all sockets - if you intend to perform any timed actions more frequently you should change this
	356	+			// that said it's likely we'll loop more often than this due to there being data waiting on one of the sockets but there aren't any guarantees
	357	+	
	358	+			if( gGHost->Update( 50000 ) )
	359	+				break;
	360	+		}
	361	+	
	362	+		// shutdown ghost
	363	+	
	364	+		CONSOLE_Print( "[GHOST] shutting down" );
	365	+		delete gGHost;
	366	+		gGHost = NULL;
	367	+	
	368	+	#ifdef WIN32
	369	+		// shutdown winsock
	370	+	
	371	+		CONSOLE_Print( "[GHOST] shutting down winsock" );
	372	+		WSACleanup( );
	373	+	
	374	+		// shutdown timer
	375	+	
	376	+		timeEndPeriod( TimerResolution );
	377	+	#endif
	378	+	
	379	+		if( gLog )
	380	+		{
	381	+			if( !gLog->fail( ) )
	382	+				gLog->close( );
	383	+	
	384	+			delete gLog;
	385	+		}
	386	+	
	387	+		return 0;
	388	+	}
	389	+	
	390	+	//
	391	+	// CGHost
	392	+	//
	393	+	
	394	+	CGHost :: CGHost( CConfig *CFG )
	395	+	{
	396	+		m_UDPSocket = new CUDPSocket( );
	397	+		m_UDPSocket->SetBroadcastTarget( CFG->GetString( "udp_broadcasttarget", string( ) ) );
	398	+		m_UDPSocket->SetDontRoute( CFG->GetInt( "udp_dontroute", 0 ) == 0 ? false : true );
	399	+		m_ReconnectSocket = NULL;
	400	+		m_GPSProtocol = new CGPSProtocol( );
	401	+		m_CRC = new CCRC32( );
	402	+		m_CRC->Initialize( );
	403	+		m_SHA = new CSHA1( );
	404	+		m_CurrentGame = NULL;
	405	+		string DBType = CFG->GetString( "db_type", "sqlite3" );
	406	+		CONSOLE_Print( "[GHOST] opening primary database" );
	407	+	
	408	+		if( DBType == "mysql" )
	409	+		{
	410	+	#ifdef GHOST_MYSQL
	411	+			m_DB = new CGHostDBMySQL( CFG );
	412	+	#else
	413	+			CONSOLE_Print( "[GHOST] warning - this binary was not compiled with MySQL database support, using SQLite database instead" );
	414	+			m_DB = new CGHostDBSQLite( CFG );
	415	+	#endif
	416	+		}
	417	+		else
	418	+			m_DB = new CGHostDBSQLite( CFG );
	419	+	
	420	+		CONSOLE_Print( "[GHOST] opening secondary (local) database" );
	421	+		m_DBLocal = new CGHostDBSQLite( CFG );
	422	+	
	423	+		// get a list of local IP addresses
	424	+		// this list is used elsewhere to determine if a player connecting to the bot is local or not
	425	+	
	426	+		CONSOLE_Print( "[GHOST] attempting to find local IP addresses" );
	427	+	
	428	+	#ifdef WIN32
	429	+		// use a more reliable Windows specific method since the portable method doesn't always work properly on Windows
	430	+		// code stolen from: http://tangentsoft.net/wskfaq/examples/getifaces.html
	431	+	
	432	+		SOCKET sd = WSASocket( AF_INET, SOCK_DGRAM, 0, 0, 0, 0 );
	433	+	
	434	+		if( sd == SOCKET_ERROR )
	435	+			CONSOLE_Print( "[GHOST] error finding local IP addresses - failed to create socket (error code " + UTIL_ToString( WSAGetLastError( ) ) + ")" );
	436	+		else
	437	+		{
	438	+			INTERFACE_INFO InterfaceList[20];
	439	+			unsigned long nBytesReturned;
	440	+	
	441	+			if( WSAIoctl( sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, sizeof(InterfaceList), &nBytesReturned, 0, 0 ) == SOCKET_ERROR )
	442	+				CONSOLE_Print( "[GHOST] error finding local IP addresses - WSAIoctl failed (error code " + UTIL_ToString( WSAGetLastError( ) ) + ")" );
	443	+			else
	444	+			{
	445	+				int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
	446	+	
	447	+	                        for( int i = 0; i < nNumInterfaces; ++i )
	448	+				{
	449	+					sockaddr_in *pAddress;
	450	+					pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
	451	+					CONSOLE_Print( "[GHOST] local IP address #" + UTIL_ToString( i + 1 ) + " is [" + string( inet_ntoa( pAddress->sin_addr ) ) + "]" );
	452	+					m_LocalAddresses.push_back( UTIL_CreateByteArray( (uint32_t)pAddress->sin_addr.s_addr, false ) );
	453	+				}
	454	+			}
	455	+	
	456	+			closesocket( sd );
	457	+		}
	458	+	#else
	459	+		// use a portable method
	460	+	
	461	+		char HostName[255];
	462	+	
	463	+		if( gethostname( HostName, 255 ) == SOCKET_ERROR )
	464	+			CONSOLE_Print( "[GHOST] error finding local IP addresses - failed to get local hostname" );
	465	+		else
	466	+		{
	467	+			CONSOLE_Print( "[GHOST] local hostname is [" + string( HostName ) + "]" );
	468	+			struct hostent *HostEnt = gethostbyname( HostName );
	469	+	
	470	+			if( !HostEnt )
	471	+				CONSOLE_Print( "[GHOST] error finding local IP addresses - gethostbyname failed" );
	472	+			else
	473	+			{
	474	+	                        for( int i = 0; HostEnt->h_addr_list[i] != NULL; ++i )
	475	+				{
	476	+					struct in_addr Address;
	477	+					memcpy( &Address, HostEnt->h_addr_list[i], sizeof(struct in_addr) );
	478	+					CONSOLE_Print( "[GHOST] local IP address #" + UTIL_ToString( i + 1 ) + " is [" + string( inet_ntoa( Address ) ) + "]" );
	479	+					m_LocalAddresses.push_back( UTIL_CreateByteArray( (uint32_t)Address.s_addr, false ) );
	480	+				}
	481	+			}
	482	+		}
	483	+	#endif
	484	+	
	485	+		m_Language = NULL;
	486	+		m_Exiting = false;
	487	+		m_ExitingNice = false;
	488	+		m_Enabled = true;
	489	+		m_Version = "17.2";
	490	+		m_HostCounter = 1;
	491	+		m_AutoHostMaximumGames = CFG->GetInt( "autohost_maxgames", 0 );
	492	+		m_AutoHostAutoStartPlayers = CFG->GetInt( "autohost_startplayers", 0 );
	493	+		m_AutoHostGameName = CFG->GetString( "autohost_gamename", string( ) );
	494	+		m_AutoHostOwner = CFG->GetString( "autohost_owner", string( ) );
	495	+		m_LastAutoHostTime = GetTime( );
	496	+		m_AutoHostMatchMaking = false;
	497	+		m_AutoHostMinimumScore = 0.0;
	498	+		m_AutoHostMaximumScore = 0.0;
	499	+		m_AllGamesFinished = false;
	500	+		m_AllGamesFinishedTime = 0;
	501	+		m_TFT = CFG->GetInt( "bot_tft", 1 ) == 0 ? false : true;
	502	+	
	503	+		if( m_TFT )
	504	+			CONSOLE_Print( "[GHOST] acting as Warcraft III: The Frozen Throne" );
	505	+		else
	506	+			CONSOLE_Print( "[GHOST] acting as Warcraft III: Reign of Chaos" );
	507	+	
	508	+		m_HostPort = CFG->GetInt( "bot_hostport", 6112 );
	509	+		m_Reconnect = CFG->GetInt( "bot_reconnect", 1 ) == 0 ? false : true;
	510	+		m_ReconnectPort = CFG->GetInt( "bot_reconnectport", 6114 );
	511	+		m_DefaultMap = CFG->GetString( "bot_defaultmap", "map" );
	512	+		m_AdminGameCreate = CFG->GetInt( "admingame_create", 0 ) == 0 ? false : true;
	513	+		m_AdminGamePort = CFG->GetInt( "admingame_port", 6113 );
	514	+		m_AdminGamePassword = CFG->GetString( "admingame_password", string( ) );
	515	+		m_AdminGameMap = CFG->GetString( "admingame_map", string( ) );
	516	+		m_LANWar3Version = CFG->GetInt( "lan_war3version", 26 );
	517	+		m_ReplayWar3Version = CFG->GetInt( "replay_war3version", 26 );
	518	+		m_ReplayBuildNumber = CFG->GetInt( "replay_buildnumber", 6059 );
	519	+		SetConfigs( CFG );
	520	+	
	521	+		// load the battle.net connections
	522	+		// we're just loading the config data and creating the CBNET classes here, the connections are established later (in the Update function)
	523	+	
	524	+	        for( uint32_t i = 1; i < 10; ++i )
	525	+		{
	526	+			string Prefix;
	527	+	
	528	+			if( i == 1 )
	529	+				Prefix = "bnet_";
	530	+			else
	531	+				Prefix = "bnet" + UTIL_ToString( i ) + "_";
	532	+	
	533	+			string Server = CFG->GetString( Prefix + "server", string( ) );
	534	+			string ServerAlias = CFG->GetString( Prefix + "serveralias", string( ) );
	535	+			string CDKeyROC = CFG->GetString( Prefix + "cdkeyroc", string( ) );
	536	+			string CDKeyTFT = CFG->GetString( Prefix + "cdkeytft", string( ) );
	537	+			string CountryAbbrev = CFG->GetString( Prefix + "countryabbrev", "USA" );
	538	+			string Country = CFG->GetString( Prefix + "country", "United States" );
	539	+			string Locale = CFG->GetString( Prefix + "locale", "system" );
	540	+			uint32_t LocaleID;
	541	+	
	542	+			if( Locale == "system" )
	543	+			{
	544	+	#ifdef WIN32
	545	+				LocaleID = GetUserDefaultLangID( );
	546	+	#else
	547	+				LocaleID = 1033;
	548	+	#endif
	549	+			}
	550	+			else
	551	+				LocaleID = UTIL_ToUInt32( Locale );
	552	+	
	553	+			string UserName = CFG->GetString( Prefix + "username", string( ) );
	554	+			string UserPassword = CFG->GetString( Prefix + "password", string( ) );
	555	+			string FirstChannel = CFG->GetString( Prefix + "firstchannel", "The Void" );
	556	+			string RootAdmin = CFG->GetString( Prefix + "rootadmin", string( ) );
	557	+			string BNETCommandTrigger = CFG->GetString( Prefix + "commandtrigger", "!" );
	558	+	
	559	+			if( BNETCommandTrigger.empty( ) )
	560	+				BNETCommandTrigger = "!";
	561	+	
	562	+			bool HoldFriends = CFG->GetInt( Prefix + "holdfriends", 1 ) == 0 ? false : true;
	563	+			bool HoldClan = CFG->GetInt( Prefix + "holdclan", 1 ) == 0 ? false : true;
	564	+			bool PublicCommands = CFG->GetInt( Prefix + "publiccommands", 1 ) == 0 ? false : true;
	565	+			string BNLSServer = CFG->GetString( Prefix + "bnlsserver", string( ) );
	566	+			int BNLSPort = CFG->GetInt( Prefix + "bnlsport", 9367 );
	567	+			int BNLSWardenCookie = CFG->GetInt( Prefix + "bnlswardencookie", 0 );
	568	+			unsigned char War3Version = CFG->GetInt( Prefix + "custom_war3version", 26 );
	569	+			BYTEARRAY EXEVersion = UTIL_ExtractNumbers( CFG->GetString( Prefix + "custom_exeversion", string( ) ), 4 );
	570	+			BYTEARRAY EXEVersionHash = UTIL_ExtractNumbers( CFG->GetString( Prefix + "custom_exeversionhash", string( ) ), 4 );
	571	+			string PasswordHashType = CFG->GetString( Prefix + "custom_passwordhashtype", string( ) );
	572	+			string PVPGNRealmName = CFG->GetString( Prefix + "custom_pvpgnrealmname", "PvPGN Realm" );
	573	+			uint32_t MaxMessageLength = CFG->GetInt( Prefix + "custom_maxmessagelength", 200 );
	574	+	
	575	+			if( Server.empty( ) )
	576	+				break;
	577	+	
	578	+			if( CDKeyROC.empty( ) )
	579	+			{
	580	+				CONSOLE_Print( "[GHOST] missing " + Prefix + "cdkeyroc, skipping this battle.net connection" );
	581	+				continue;
	582	+			}
	583	+	
	584	+			if( m_TFT && CDKeyTFT.empty( ) )
	585	+			{
	586	+				CONSOLE_Print( "[GHOST] missing " + Prefix + "cdkeytft, skipping this battle.net connection" );
	587	+				continue;
	588	+			}
	589	+	
	590	+			if( UserName.empty( ) )
	591	+			{
	592	+				CONSOLE_Print( "[GHOST] missing " + Prefix + "username, skipping this battle.net connection" );
	593	+				continue;
	594	+			}
	595	+	
	596	+			if( UserPassword.empty( ) )
	597	+			{
	598	+				CONSOLE_Print( "[GHOST] missing " + Prefix + "password, skipping this battle.net connection" );
	599	+				continue;
	600	+			}
	601	+	
	602	+			CONSOLE_Print( "[GHOST] found battle.net connection #" + UTIL_ToString( i ) + " for server [" + Server + "]" );
	603	+	
	604	+			if( Locale == "system" )
	605	+			{
	606	+	#ifdef WIN32
	607	+				CONSOLE_Print( "[GHOST] using system locale of " + UTIL_ToString( LocaleID ) );
	608	+	#else
	609	+				CONSOLE_Print( "[GHOST] unable to get system locale, using default locale of 1033" );
	610	+	#endif
	611	+			}
	612	+	
	613	+			m_BNETs.push_back( new CBNET( this, Server, ServerAlias, BNLSServer, (uint16_t)BNLSPort, (uint32_t)BNLSWardenCookie, CDKeyROC, CDKeyTFT, CountryAbbrev, Country, LocaleID, UserName, UserPassword, FirstChannel, RootAdmin, BNETCommandTrigger[0], HoldFriends, HoldClan, PublicCommands, War3Version, EXEVersion, EXEVersionHash, PasswordHashType, PVPGNRealmName, MaxMessageLength, i ) );
	614	+		}
	615	+	
	616	+		if( m_BNETs.empty( ) )
	617	+			CONSOLE_Print( "[GHOST] warning - no battle.net connections found in config file" );
	618	+	
	619	+		// extract common.j and blizzard.j from War3Patch.mpq if we can
	620	+		// these two files are necessary for calculating "map_crc" when loading maps so we make sure to do it before loading the default map
	621	+		// see CMap :: Load for more information
	622	+	
	623	+		ExtractScripts( );
	624	+	
	625	+		// load the default maps (note: make sure to run ExtractScripts first)
	626	+	
	627	+		if( m_DefaultMap.size( ) < 4 || m_DefaultMap.substr( m_DefaultMap.size( ) - 4 ) != ".cfg" )
	628	+		{
	629	+			m_DefaultMap += ".cfg";
	630	+			CONSOLE_Print( "[GHOST] adding \".cfg\" to default map -> new default is [" + m_DefaultMap + "]" );
	631	+		}
	632	+	
	633	+		CConfig MapCFG;
	634	+		MapCFG.Read( m_MapCFGPath + m_DefaultMap );
	635	+		m_Map = new CMap( this, &MapCFG, m_MapCFGPath + m_DefaultMap );
	636	+	
	637	+		if( !m_AdminGameMap.empty( ) )
	638	+		{
	639	+			if( m_AdminGameMap.size( ) < 4 || m_AdminGameMap.substr( m_AdminGameMap.size( ) - 4 ) != ".cfg" )
	640	+			{
	641	+				m_AdminGameMap += ".cfg";
	642	+				CONSOLE_Print( "[GHOST] adding \".cfg\" to default admin game map -> new default is [" + m_AdminGameMap + "]" );
	643	+			}
	644	+	
	645	+			CONSOLE_Print( "[GHOST] trying to load default admin game map" );
	646	+			CConfig AdminMapCFG;
	647	+			AdminMapCFG.Read( m_MapCFGPath + m_AdminGameMap );
	648	+			m_AdminMap = new CMap( this, &AdminMapCFG, m_MapCFGPath + m_AdminGameMap );
	649	+	
	650	+			if( !m_AdminMap->GetValid( ) )
	651	+			{
	652	+				CONSOLE_Print( "[GHOST] default admin game map isn't valid, using hardcoded admin game map instead" );
	653	+				delete m_AdminMap;
	654	+				m_AdminMap = new CMap( this );
	655	+			}
	656	+		}
	657	+		else
	658	+		{
	659	+			CONSOLE_Print( "[GHOST] using hardcoded admin game map" );
	660	+			m_AdminMap = new CMap( this );
	661	+		}
	662	+	
	663	+		m_AutoHostMap = new CMap( *m_Map );
	664	+		m_SaveGame = new CSaveGame( );
	665	+	
	666	+		// load the iptocountry data
	667	+	
	668	+		LoadIPToCountryData( );
	669	+	
	670	+		// create the admin game
	671	+	
	672	+		if( m_AdminGameCreate )
	673	+		{
	674	+			CONSOLE_Print( "[GHOST] creating admin game" );
	675	+			m_AdminGame = new CAdminGame( this, m_AdminMap, NULL, m_AdminGamePort, 0, "GHost++ Admin Game", m_AdminGamePassword );
	676	+	
	677	+			if( m_AdminGamePort == m_HostPort )
	678	+				CONSOLE_Print( "[GHOST] warning - admingame_port and bot_hostport are set to the same value, you won't be able to host any games" );
	679	+		}
	680	+		else
	681	+			m_AdminGame = NULL;
	682	+	
	683	+		if( m_BNETs.empty( ) && !m_AdminGame )
	684	+			CONSOLE_Print( "[GHOST] warning - no battle.net connections found and no admin game created" );
	685	+	
	686	+	#ifdef GHOST_MYSQL
	687	+		CONSOLE_Print( "[GHOST] GHost++ Version " + m_Version + " (with MySQL support)" );
	688	+	#else
	689	+		CONSOLE_Print( "[GHOST] GHost++ Version " + m_Version + " (without MySQL support)" );
	690	+	#endif
	691	+	}
	692	+	
	693	+	CGHost :: ~CGHost( )
	694	+	{
	695	+		delete m_UDPSocket;
	696	+		delete m_ReconnectSocket;
	697	+	
	698	+	        for( vector<CTCPSocket *> :: iterator i = m_ReconnectSockets.begin( ); i != m_ReconnectSockets.end( ); ++i )
	699	+			delete *i;
	700	+	
	701	+		delete m_GPSProtocol;
	702	+		delete m_CRC;
	703	+		delete m_SHA;
	704	+	
	705	+	        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	706	+			delete *i;
	707	+	
	708	+		delete m_CurrentGame;
	709	+		delete m_AdminGame;
	710	+	
	711	+	        for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); ++i )
	712	+			delete *i;
	713	+	
	714	+		delete m_DB;
	715	+		delete m_DBLocal;
	716	+	
	717	+		// warning: we don't delete any entries of m_Callables here because we can't be guaranteed that the associated threads have terminated
	718	+		// this is fine if the program is currently exiting because the OS will clean up after us
	719	+		// but if you try to recreate the CGHost object within a single session you will probably leak resources!
	720	+	
	721	+		if( !m_Callables.empty( ) )
	722	+			CONSOLE_Print( "[GHOST] warning - " + UTIL_ToString( m_Callables.size( ) ) + " orphaned callables were leaked (this is not an error)" );
	723	+	
	724	+		delete m_Language;
	725	+		delete m_Map;
	726	+		delete m_AdminMap;
	727	+		delete m_AutoHostMap;
	728	+		delete m_SaveGame;
	729	+	}
	730	+	
	731	+	bool CGHost :: Update( long usecBlock )
	732	+	{
	733	+		// todotodo: do we really want to shutdown if there's a database error? is there any way to recover from this?
	734	+	
	735	+		if( m_DB->HasError( ) )
	736	+		{
	737	+			CONSOLE_Print( "[GHOST] database error - " + m_DB->GetError( ) );
	738	+			return true;
	739	+		}
	740	+	
	741	+		if( m_DBLocal->HasError( ) )
	742	+		{
	743	+			CONSOLE_Print( "[GHOST] local database error - " + m_DBLocal->GetError( ) );
	744	+			return true;
	745	+		}
	746	+	
	747	+		// try to exit nicely if requested to do so
	748	+	
	749	+		if( m_ExitingNice )
	750	+		{
	751	+			if( !m_BNETs.empty( ) )
	752	+			{
	753	+				CONSOLE_Print( "[GHOST] deleting all battle.net connections in preparation for exiting nicely" );
	754	+	
	755	+	                        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	756	+					delete *i;
	757	+	
	758	+				m_BNETs.clear( );
	759	+			}
	760	+	
	761	+			if( m_CurrentGame )
	762	+			{
	763	+				CONSOLE_Print( "[GHOST] deleting current game in preparation for exiting nicely" );
	764	+				delete m_CurrentGame;
	765	+				m_CurrentGame = NULL;
	766	+			}
	767	+	
	768	+			if( m_AdminGame )
	769	+			{
	770	+				CONSOLE_Print( "[GHOST] deleting admin game in preparation for exiting nicely" );
	771	+				delete m_AdminGame;
	772	+				m_AdminGame = NULL;
	773	+			}
	774	+	
	775	+			if( m_Games.empty( ) )
	776	+			{
	777	+				if( !m_AllGamesFinished )
	778	+				{
	779	+					CONSOLE_Print( "[GHOST] all games finished, waiting 60 seconds for threads to finish" );
	780	+					CONSOLE_Print( "[GHOST] there are " + UTIL_ToString( m_Callables.size( ) ) + " threads in progress" );
	781	+					m_AllGamesFinished = true;
	782	+					m_AllGamesFinishedTime = GetTime( );
	783	+				}
	784	+				else
	785	+				{
	786	+					if( m_Callables.empty( ) )
	787	+					{
	788	+						CONSOLE_Print( "[GHOST] all threads finished, exiting nicely" );
	789	+						m_Exiting = true;
	790	+					}
	791	+					else if( GetTime( ) - m_AllGamesFinishedTime >= 60 )
	792	+					{
	793	+						CONSOLE_Print( "[GHOST] waited 60 seconds for threads to finish, exiting anyway" );
	794	+						CONSOLE_Print( "[GHOST] there are " + UTIL_ToString( m_Callables.size( ) ) + " threads still in progress which will be terminated" );
	795	+						m_Exiting = true;
	796	+					}
	797	+				}
	798	+			}
	799	+		}
	800	+	
	801	+		// update callables
	802	+	
	803	+		for( vector<CBaseCallable *> :: iterator i = m_Callables.begin( ); i != m_Callables.end( ); )
	804	+		{
	805	+			if( (*i)->GetReady( ) )
	806	+			{
	807	+				m_DB->RecoverCallable( *i );
	808	+				delete *i;
	809	+				i = m_Callables.erase( i );
	810	+			}
	811	+			else
	812	+	                        ++i;
	813	+		}
	814	+	
	815	+		// create the GProxy++ reconnect listener
	816	+	
	817	+		if( m_Reconnect )
	818	+		{
	819	+			if( !m_ReconnectSocket )
	820	+			{
	821	+				m_ReconnectSocket = new CTCPServer( );
	822	+	
	823	+				if( m_ReconnectSocket->Listen( m_BindAddress, m_ReconnectPort ) )
	824	+					CONSOLE_Print( "[GHOST] listening for GProxy++ reconnects on port " + UTIL_ToString( m_ReconnectPort ) );
	825	+				else
	826	+				{
	827	+					CONSOLE_Print( "[GHOST] error listening for GProxy++ reconnects on port " + UTIL_ToString( m_ReconnectPort ) );
	828	+					delete m_ReconnectSocket;
	829	+					m_ReconnectSocket = NULL;
	830	+					m_Reconnect = false;
	831	+				}
	832	+			}
	833	+			else if( m_ReconnectSocket->HasError( ) )
	834	+			{
	835	+				CONSOLE_Print( "[GHOST] GProxy++ reconnect listener error (" + m_ReconnectSocket->GetErrorString( ) + ")" );
	836	+				delete m_ReconnectSocket;
	837	+				m_ReconnectSocket = NULL;
	838	+				m_Reconnect = false;
	839	+			}
	840	+		}
	841	+	
	842	+		unsigned int NumFDs = 0;
	843	+	
	844	+		// take every socket we own and throw it in one giant select statement so we can block on all sockets
	845	+	
	846	+		int nfds = 0;
	847	+		fd_set fd;
	848	+		fd_set send_fd;
	849	+		FD_ZERO( &fd );
	850	+		FD_ZERO( &send_fd );
	851	+	
	852	+		// 1. all battle.net sockets
	853	+	
	854	+	        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	855	+			NumFDs += (*i)->SetFD( &fd, &send_fd, &nfds );
	856	+	
	857	+		// 2. the current game's server and player sockets
	858	+	
	859	+		if( m_CurrentGame )
	860	+			NumFDs += m_CurrentGame->SetFD( &fd, &send_fd, &nfds );
	861	+	
	862	+		// 3. the admin game's server and player sockets
	863	+	
	864	+		if( m_AdminGame )
	865	+			NumFDs += m_AdminGame->SetFD( &fd, &send_fd, &nfds );
	866	+	
	867	+		// 4. all running games' player sockets
	868	+	
	869	+	        for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); ++i )
	870	+			NumFDs += (*i)->SetFD( &fd, &send_fd, &nfds );
	871	+	
	872	+		// 5. the GProxy++ reconnect socket(s)
	873	+	
	874	+		if( m_Reconnect && m_ReconnectSocket )
	875	+		{
	876	+			m_ReconnectSocket->SetFD( &fd, &send_fd, &nfds );
	877	+	                ++NumFDs;
	878	+		}
	879	+	
	880	+	        for( vector<CTCPSocket *> :: iterator i = m_ReconnectSockets.begin( ); i != m_ReconnectSockets.end( ); ++i )
	881	+		{
	882	+			(*i)->SetFD( &fd, &send_fd, &nfds );
	883	+	                ++NumFDs;
	884	+		}
	885	+	
	886	+		// before we call select we need to determine how long to block for
	887	+		// previously we just blocked for a maximum of the passed usecBlock microseconds
	888	+		// however, in an effort to make game updates happen closer to the desired latency setting we now use a dynamic block interval
	889	+		// note: we still use the passed usecBlock as a hard maximum
	890	+	
	891	+	        for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); ++i )
	892	+		{
	893	+			if( (*i)->GetNextTimedActionTicks( ) * 1000 < usecBlock )
	894	+				usecBlock = (*i)->GetNextTimedActionTicks( ) * 1000;
	895	+		}
	896	+	
	897	+		// always block for at least 1ms just in case something goes wrong
	898	+		// this prevents the bot from sucking up all the available CPU if a game keeps asking for immediate updates
	899	+		// it's a bit ridiculous to include this check since, in theory, the bot is programmed well enough to never make this mistake
	900	+		// however, considering who programmed it, it's worthwhile to do it anyway
	901	+	
	902	+		if( usecBlock < 1000 )
	903	+			usecBlock = 1000;
	904	+	
	905	+		struct timeval tv;
	906	+		tv.tv_sec = 0;
	907	+		tv.tv_usec = usecBlock;
	908	+	
	909	+		struct timeval send_tv;
	910	+		send_tv.tv_sec = 0;
	911	+		send_tv.tv_usec = 0;
	912	+	
	913	+	#ifdef WIN32
	914	+		select( 1, &fd, NULL, NULL, &tv );
	915	+		select( 1, NULL, &send_fd, NULL, &send_tv );
	916	+	#else
	917	+		select( nfds + 1, &fd, NULL, NULL, &tv );
	918	+		select( nfds + 1, NULL, &send_fd, NULL, &send_tv );
	919	+	#endif
	920	+	
	921	+		if( NumFDs == 0 )
	922	+		{
	923	+			// we don't have any sockets (i.e. we aren't connected to battle.net maybe due to a lost connection and there aren't any games running)
	924	+			// select will return immediately and we'll chew up the CPU if we let it loop so just sleep for 50ms to kill some time
	925	+	
	926	+			MILLISLEEP( 50 );
	927	+		}
	928	+	
	929	+		bool AdminExit = false;
	930	+		bool BNETExit = false;
	931	+	
	932	+		// update current game
	933	+	
	934	+		if( m_CurrentGame )
	935	+		{
	936	+			if( m_CurrentGame->Update( &fd, &send_fd ) )
	937	+			{
	938	+				CONSOLE_Print( "[GHOST] deleting current game [" + m_CurrentGame->GetGameName( ) + "]" );
	939	+				delete m_CurrentGame;
	940	+				m_CurrentGame = NULL;
	941	+	
	942	+	                        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	943	+				{
	944	+					(*i)->QueueGameUncreate( );
	945	+					(*i)->QueueEnterChat( );
	946	+				}
	947	+			}
	948	+			else if( m_CurrentGame )
	949	+				m_CurrentGame->UpdatePost( &send_fd );
	950	+		}
	951	+	
	952	+		// update admin game
	953	+	
	954	+		if( m_AdminGame )
	955	+		{
	956	+			if( m_AdminGame->Update( &fd, &send_fd ) )
	957	+			{
	958	+				CONSOLE_Print( "[GHOST] deleting admin game" );
	959	+				delete m_AdminGame;
	960	+				m_AdminGame = NULL;
	961	+				AdminExit = true;
	962	+			}
	963	+			else if( m_AdminGame )
	964	+				m_AdminGame->UpdatePost( &send_fd );
	965	+		}
	966	+	
	967	+		// update running games
	968	+	
	969	+		for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); )
	970	+		{
	971	+			if( (*i)->Update( &fd, &send_fd ) )
	972	+			{
	973	+				CONSOLE_Print( "[GHOST] deleting game [" + (*i)->GetGameName( ) + "]" );
	974	+				EventGameDeleted( *i );
	975	+				delete *i;
	976	+				i = m_Games.erase( i );
	977	+			}
	978	+			else
	979	+			{
	980	+				(*i)->UpdatePost( &send_fd );
	981	+	                        ++i;
	982	+			}
	983	+		}
	984	+	
	985	+		// update battle.net connections
	986	+	
	987	+	        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	988	+		{
	989	+			if( (*i)->Update( &fd, &send_fd ) )
	990	+				BNETExit = true;
	991	+		}
	992	+	
	993	+		// update GProxy++ reliable reconnect sockets
	994	+	
	995	+		if( m_Reconnect && m_ReconnectSocket )
	996	+		{
	997	+			CTCPSocket *NewSocket = m_ReconnectSocket->Accept( &fd );
	998	+	
	999	+			if( NewSocket )
	1000	+				m_ReconnectSockets.push_back( NewSocket );
	1001	+		}
	1002	+	
	1003	+		for( vector<CTCPSocket *> :: iterator i = m_ReconnectSockets.begin( ); i != m_ReconnectSockets.end( ); )
	1004	+		{
	1005	+			if( (*i)->HasError( ) || !(*i)->GetConnected( ) || GetTime( ) - (*i)->GetLastRecv( ) >= 10 )
	1006	+			{
	1007	+				delete *i;
	1008	+				i = m_ReconnectSockets.erase( i );
	1009	+				continue;
	1010	+			}
	1011	+	
	1012	+			(*i)->DoRecv( &fd );
	1013	+			string *RecvBuffer = (*i)->GetBytes( );
	1014	+			BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) );
	1015	+	
	1016	+			// a packet is at least 4 bytes
	1017	+	
	1018	+			if( Bytes.size( ) >= 4 )
	1019	+			{
	1020	+				if( Bytes[0] == GPS_HEADER_CONSTANT )
	1021	+				{
	1022	+					// bytes 2 and 3 contain the length of the packet
	1023	+	
	1024	+					uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 );
	1025	+	
	1026	+					if( Length >= 4 )
	1027	+					{
	1028	+						if( Bytes.size( ) >= Length )
	1029	+						{
	1030	+							if( Bytes[1] == CGPSProtocol :: GPS_RECONNECT && Length == 13 )
	1031	+							{
	1032	+								unsigned char PID = Bytes[4];
	1033	+								uint32_t ReconnectKey = UTIL_ByteArrayToUInt32( Bytes, false, 5 );
	1034	+								uint32_t LastPacket = UTIL_ByteArrayToUInt32( Bytes, false, 9 );
	1035	+	
	1036	+								// look for a matching player in a running game
	1037	+	
	1038	+								CGamePlayer *Match = NULL;
	1039	+	
	1040	+	                                                        for( vector<CBaseGame *> :: iterator j = m_Games.begin( ); j != m_Games.end( ); ++j )
	1041	+								{
	1042	+									if( (*j)->GetGameLoaded( ) )
	1043	+									{
	1044	+										CGamePlayer *Player = (*j)->GetPlayerFromPID( PID );
	1045	+	
	1046	+										if( Player && Player->GetGProxy( ) && Player->GetGProxyReconnectKey( ) == ReconnectKey )
	1047	+										{
	1048	+											Match = Player;
	1049	+											break;
	1050	+										}
	1051	+									}
	1052	+								}
	1053	+	
	1054	+								if( Match )
	1055	+								{
	1056	+									// reconnect successful!
	1057	+	
	1058	+									*RecvBuffer = RecvBuffer->substr( Length );
	1059	+									Match->EventGProxyReconnect( *i, LastPacket );
	1060	+									i = m_ReconnectSockets.erase( i );
	1061	+									continue;
	1062	+								}
	1063	+								else
	1064	+								{
	1065	+									(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_NOTFOUND ) );
	1066	+									(*i)->DoSend( &send_fd );
	1067	+									delete *i;
	1068	+									i = m_ReconnectSockets.erase( i );
	1069	+									continue;
	1070	+								}
	1071	+							}
	1072	+							else
	1073	+							{
	1074	+								(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_INVALID ) );
	1075	+								(*i)->DoSend( &send_fd );
	1076	+								delete *i;
	1077	+								i = m_ReconnectSockets.erase( i );
	1078	+								continue;
	1079	+							}
	1080	+						}
	1081	+					}
	1082	+					else
	1083	+					{
	1084	+						(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_INVALID ) );
	1085	+						(*i)->DoSend( &send_fd );
	1086	+						delete *i;
	1087	+						i = m_ReconnectSockets.erase( i );
	1088	+						continue;
	1089	+					}
	1090	+				}
	1091	+				else
	1092	+				{
	1093	+					(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_INVALID ) );
	1094	+					(*i)->DoSend( &send_fd );
	1095	+					delete *i;
	1096	+					i = m_ReconnectSockets.erase( i );
	1097	+					continue;
	1098	+				}
	1099	+			}
	1100	+	
	1101	+			(*i)->DoSend( &send_fd );
	1102	+	                ++i;
	1103	+		}
	1104	+	
	1105	+		// autohost
	1106	+	
	1107	+		if( !m_AutoHostGameName.empty( ) && m_AutoHostMaximumGames != 0 && m_AutoHostAutoStartPlayers != 0 && GetTime( ) - m_LastAutoHostTime >= 30 )
	1108	+		{
	1109	+			// copy all the checks from CGHost :: CreateGame here because we don't want to spam the chat when there's an error
	1110	+			// instead we fail silently and try again soon
	1111	+	
	1112	+			if( !m_ExitingNice && m_Enabled && !m_CurrentGame && m_Games.size( ) < m_MaxGames && m_Games.size( ) < m_AutoHostMaximumGames )
	1113	+			{
	1114	+				if( m_AutoHostMap->GetValid( ) )
	1115	+				{
	1116	+					string GameName = m_AutoHostGameName + " #" + UTIL_ToString( m_HostCounter );
	1117	+	
	1118	+					if( GameName.size( ) <= 31 )
	1119	+					{
	1120	+						CreateGame( m_AutoHostMap, GAME_PUBLIC, false, GameName, m_AutoHostOwner, m_AutoHostOwner, m_AutoHostServer, false );
	1121	+	
	1122	+						if( m_CurrentGame )
	1123	+						{
	1124	+							m_CurrentGame->SetAutoStartPlayers( m_AutoHostAutoStartPlayers );
	1125	+	
	1126	+							if( m_AutoHostMatchMaking )
	1127	+							{
	1128	+								if( !m_Map->GetMapMatchMakingCategory( ).empty( ) )
	1129	+								{
	1130	+									if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) )
	1131	+										CONSOLE_Print( "[GHOST] autohostmm - map_matchmakingcategory [" + m_Map->GetMapMatchMakingCategory( ) + "] found but matchmaking can only be used with fixed player settings, matchmaking disabled" );
	1132	+									else
	1133	+									{
	1134	+										CONSOLE_Print( "[GHOST] autohostmm - map_matchmakingcategory [" + m_Map->GetMapMatchMakingCategory( ) + "] found, matchmaking enabled" );
	1135	+	
	1136	+										m_CurrentGame->SetMatchMaking( true );
	1137	+										m_CurrentGame->SetMinimumScore( m_AutoHostMinimumScore );
	1138	+										m_CurrentGame->SetMaximumScore( m_AutoHostMaximumScore );
	1139	+									}
	1140	+								}
	1141	+								else
	1142	+									CONSOLE_Print( "[GHOST] autohostmm - map_matchmakingcategory not found, matchmaking disabled" );
	1143	+							}
	1144	+						}
	1145	+					}
	1146	+					else
	1147	+					{
	1148	+						CONSOLE_Print( "[GHOST] stopped auto hosting, next game name [" + GameName + "] is too long (the maximum is 31 characters)" );
	1149	+						m_AutoHostGameName.clear( );
	1150	+						m_AutoHostOwner.clear( );
	1151	+						m_AutoHostServer.clear( );
	1152	+						m_AutoHostMaximumGames = 0;
	1153	+						m_AutoHostAutoStartPlayers = 0;
	1154	+						m_AutoHostMatchMaking = false;
	1155	+						m_AutoHostMinimumScore = 0.0;
	1156	+						m_AutoHostMaximumScore = 0.0;
	1157	+					}
	1158	+				}
	1159	+				else
	1160	+				{
	1161	+					CONSOLE_Print( "[GHOST] stopped auto hosting, map config file [" + m_AutoHostMap->GetCFGFile( ) + "] is invalid" );
	1162	+					m_AutoHostGameName.clear( );
	1163	+					m_AutoHostOwner.clear( );
	1164	+					m_AutoHostServer.clear( );
	1165	+					m_AutoHostMaximumGames = 0;
	1166	+					m_AutoHostAutoStartPlayers = 0;
	1167	+					m_AutoHostMatchMaking = false;
	1168	+					m_AutoHostMinimumScore = 0.0;
	1169	+					m_AutoHostMaximumScore = 0.0;
	1170	+				}
	1171	+			}
	1172	+	
	1173	+			m_LastAutoHostTime = GetTime( );
	1174	+		}
	1175	+	
	1176	+		return m_Exiting || AdminExit || BNETExit;
	1177	+	}
	1178	+	
	1179	+	void CGHost :: EventBNETConnecting( CBNET *bnet )
	1180	+	{
	1181	+		if( m_AdminGame )
	1182	+			m_AdminGame->SendAllChat( m_Language->ConnectingToBNET( bnet->GetServer( ) ) );
	1183	+	
	1184	+		if( m_CurrentGame )
	1185	+			m_CurrentGame->SendAllChat( m_Language->ConnectingToBNET( bnet->GetServer( ) ) );
	1186	+	}
	1187	+	
	1188	+	void CGHost :: EventBNETConnected( CBNET *bnet )
	1189	+	{
	1190	+		if( m_AdminGame )
	1191	+			m_AdminGame->SendAllChat( m_Language->ConnectedToBNET( bnet->GetServer( ) ) );
	1192	+	
	1193	+		if( m_CurrentGame )
	1194	+			m_CurrentGame->SendAllChat( m_Language->ConnectedToBNET( bnet->GetServer( ) ) );
	1195	+	}
	1196	+	
	1197	+	void CGHost :: EventBNETDisconnected( CBNET *bnet )
	1198	+	{
	1199	+		if( m_AdminGame )
	1200	+			m_AdminGame->SendAllChat( m_Language->DisconnectedFromBNET( bnet->GetServer( ) ) );
	1201	+	
	1202	+		if( m_CurrentGame )
	1203	+			m_CurrentGame->SendAllChat( m_Language->DisconnectedFromBNET( bnet->GetServer( ) ) );
	1204	+	}
	1205	+	
	1206	+	void CGHost :: EventBNETLoggedIn( CBNET *bnet )
	1207	+	{
	1208	+		if( m_AdminGame )
	1209	+			m_AdminGame->SendAllChat( m_Language->LoggedInToBNET( bnet->GetServer( ) ) );
	1210	+	
	1211	+		if( m_CurrentGame )
	1212	+			m_CurrentGame->SendAllChat( m_Language->LoggedInToBNET( bnet->GetServer( ) ) );
	1213	+	}
	1214	+	
	1215	+	void CGHost :: EventBNETGameRefreshed( CBNET *bnet )
	1216	+	{
	1217	+		if( m_AdminGame )
	1218	+			m_AdminGame->SendAllChat( m_Language->BNETGameHostingSucceeded( bnet->GetServer( ) ) );
	1219	+	
	1220	+		if( m_CurrentGame )
	1221	+			m_CurrentGame->EventGameRefreshed( bnet->GetServer( ) );
	1222	+	}
	1223	+	
	1224	+	void CGHost :: EventBNETGameRefreshFailed( CBNET *bnet )
	1225	+	{
	1226	+		if( m_CurrentGame )
	1227	+		{
	1228	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1229	+			{
	1230	+				(*i)->QueueChatCommand( m_Language->UnableToCreateGameTryAnotherName( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ) );
	1231	+	
	1232	+				if( (*i)->GetServer( ) == m_CurrentGame->GetCreatorServer( ) )
	1233	+					(*i)->QueueChatCommand( m_Language->UnableToCreateGameTryAnotherName( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ), m_CurrentGame->GetCreatorName( ), true );
	1234	+			}
	1235	+	
	1236	+			if( m_AdminGame )
	1237	+				m_AdminGame->SendAllChat( m_Language->BNETGameHostingFailed( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ) );
	1238	+	
	1239	+			m_CurrentGame->SendAllChat( m_Language->UnableToCreateGameTryAnotherName( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ) );
	1240	+	
	1241	+			// we take the easy route and simply close the lobby if a refresh fails
	1242	+			// it's possible at least one refresh succeeded and therefore the game is still joinable on at least one battle.net (plus on the local network) but we don't keep track of that
	1243	+			// we only close the game if it has no players since we support game rehosting (via !priv and !pub in the lobby)
	1244	+	
	1245	+			if( m_CurrentGame->GetNumHumanPlayers( ) == 0 )
	1246	+				m_CurrentGame->SetExiting( true );
	1247	+	
	1248	+			m_CurrentGame->SetRefreshError( true );
	1249	+		}
	1250	+	}
	1251	+	
	1252	+	void CGHost :: EventBNETConnectTimedOut( CBNET *bnet )
	1253	+	{
	1254	+		if( m_AdminGame )
	1255	+			m_AdminGame->SendAllChat( m_Language->ConnectingToBNETTimedOut( bnet->GetServer( ) ) );
	1256	+	
	1257	+		if( m_CurrentGame )
	1258	+			m_CurrentGame->SendAllChat( m_Language->ConnectingToBNETTimedOut( bnet->GetServer( ) ) );
	1259	+	}
	1260	+	
	1261	+	void CGHost :: EventBNETWhisper( CBNET *bnet, string user, string message )
	1262	+	{
	1263	+		if( m_AdminGame )
	1264	+		{
	1265	+			m_AdminGame->SendAdminChat( "[W: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1266	+	
	1267	+			if( m_CurrentGame )
	1268	+				m_CurrentGame->SendLocalAdminChat( "[W: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1269	+	
	1270	+	                for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); ++i )
	1271	+				(*i)->SendLocalAdminChat( "[W: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1272	+		}
	1273	+	}
	1274	+	
	1275	+	void CGHost :: EventBNETChat( CBNET *bnet, string user, string message )
	1276	+	{
	1277	+		if( m_AdminGame )
	1278	+		{
	1279	+			m_AdminGame->SendAdminChat( "[L: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1280	+	
	1281	+			if( m_CurrentGame )
	1282	+				m_CurrentGame->SendLocalAdminChat( "[L: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1283	+	
	1284	+	                for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); ++i )
	1285	+				(*i)->SendLocalAdminChat( "[L: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1286	+		}
	1287	+	}
	1288	+	
	1289	+	void CGHost :: EventBNETEmote( CBNET *bnet, string user, string message )
	1290	+	{
	1291	+		if( m_AdminGame )
	1292	+		{
	1293	+			m_AdminGame->SendAdminChat( "[E: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1294	+	
	1295	+			if( m_CurrentGame )
	1296	+				m_CurrentGame->SendLocalAdminChat( "[E: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1297	+	
	1298	+	                for( vector<CBaseGame *> :: iterator i = m_Games.begin( ); i != m_Games.end( ); ++i )
	1299	+				(*i)->SendLocalAdminChat( "[E: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
	1300	+		}
	1301	+	}
	1302	+	
	1303	+	void CGHost :: EventGameDeleted( CBaseGame *game )
	1304	+	{
	1305	+	        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1306	+		{
	1307	+			(*i)->QueueChatCommand( m_Language->GameIsOver( game->GetDescription( ) ) );
	1308	+	
	1309	+			if( (*i)->GetServer( ) == game->GetCreatorServer( ) )
	1310	+				(*i)->QueueChatCommand( m_Language->GameIsOver( game->GetDescription( ) ), game->GetCreatorName( ), true );
	1311	+		}
	1312	+	}
	1313	+	
	1314	+	void CGHost :: ReloadConfigs( )
	1315	+	{
	1316	+		CConfig CFG;
	1317	+		CFG.Read( "default.cfg" );
	1318	+		CFG.Read( gCFGFile );
	1319	+		SetConfigs( &CFG );
	1320	+	}
	1321	+	
	1322	+	void CGHost :: SetConfigs( CConfig *CFG )
	1323	+	{
	1324	+		// this doesn't set EVERY config value since that would potentially require reconfiguring the battle.net connections
	1325	+		// it just set the easily reloadable values
	1326	+	
	1327	+		m_LanguageFile = CFG->GetString( "bot_language", "language.cfg" );
	1328	+		delete m_Language;
	1329	+		m_Language = new CLanguage( m_LanguageFile );
	1330	+		m_Warcraft3Path = UTIL_AddPathSeperator( CFG->GetString( "bot_war3path", "C:\\Program Files\\Warcraft III\\" ) );
	1331	+		m_BindAddress = CFG->GetString( "bot_bindaddress", string( ) );
	1332	+		m_ReconnectWaitTime = CFG->GetInt( "bot_reconnectwaittime", 3 );
	1333	+		m_MaxGames = CFG->GetInt( "bot_maxgames", 5 );
	1334	+		string BotCommandTrigger = CFG->GetString( "bot_commandtrigger", "!" );
	1335	+	
	1336	+		if( BotCommandTrigger.empty( ) )
	1337	+			BotCommandTrigger = "!";
	1338	+	
	1339	+		m_CommandTrigger = BotCommandTrigger[0];
	1340	+		m_MapCFGPath = UTIL_AddPathSeperator( CFG->GetString( "bot_mapcfgpath", string( ) ) );
	1341	+		m_SaveGamePath = UTIL_AddPathSeperator( CFG->GetString( "bot_savegamepath", string( ) ) );
	1342	+		m_MapPath = UTIL_AddPathSeperator( CFG->GetString( "bot_mappath", string( ) ) );
	1343	+		m_SaveReplays = CFG->GetInt( "bot_savereplays", 0 ) == 0 ? false : true;
	1344	+		m_ReplayPath = UTIL_AddPathSeperator( CFG->GetString( "bot_replaypath", string( ) ) );
	1345	+		m_VirtualHostName = CFG->GetString( "bot_virtualhostname", "|cFF4080C0GHost" );
	1346	+		m_HideIPAddresses = CFG->GetInt( "bot_hideipaddresses", 0 ) == 0 ? false : true;
	1347	+		m_CheckMultipleIPUsage = CFG->GetInt( "bot_checkmultipleipusage", 1 ) == 0 ? false : true;
	1348	+	
	1349	+		if( m_VirtualHostName.size( ) > 15 )
	1350	+		{
	1351	+			m_VirtualHostName = "|cFF4080C0GHost";
	1352	+			CONSOLE_Print( "[GHOST] warning - bot_virtualhostname is longer than 15 characters, using default virtual host name" );
	1353	+		}
	1354	+	
	1355	+		m_SpoofChecks = CFG->GetInt( "bot_spoofchecks", 2 );
	1356	+		m_RequireSpoofChecks = CFG->GetInt( "bot_requirespoofchecks", 0 ) == 0 ? false : true;
	1357	+		m_ReserveAdmins = CFG->GetInt( "bot_reserveadmins", 1 ) == 0 ? false : true;
	1358	+		m_RefreshMessages = CFG->GetInt( "bot_refreshmessages", 0 ) == 0 ? false : true;
	1359	+		m_AutoLock = CFG->GetInt( "bot_autolock", 0 ) == 0 ? false : true;
	1360	+		m_AutoSave = CFG->GetInt( "bot_autosave", 0 ) == 0 ? false : true;
	1361	+		m_AllowDownloads = CFG->GetInt( "bot_allowdownloads", 0 );
	1362	+		m_PingDuringDownloads = CFG->GetInt( "bot_pingduringdownloads", 0 ) == 0 ? false : true;
	1363	+		m_MaxDownloaders = CFG->GetInt( "bot_maxdownloaders", 3 );
	1364	+		m_MaxDownloadSpeed = CFG->GetInt( "bot_maxdownloadspeed", 100 );
	1365	+		m_LCPings = CFG->GetInt( "bot_lcpings", 1 ) == 0 ? false : true;
	1366	+		m_AutoKickPing = CFG->GetInt( "bot_autokickping", 400 );
	1367	+		m_BanMethod = CFG->GetInt( "bot_banmethod", 1 );
	1368	+		m_IPBlackListFile = CFG->GetString( "bot_ipblacklistfile", "ipblacklist.txt" );
	1369	+		m_LobbyTimeLimit = CFG->GetInt( "bot_lobbytimelimit", 10 );
	1370	+		m_Latency = CFG->GetInt( "bot_latency", 100 );
	1371	+		m_SyncLimit = CFG->GetInt( "bot_synclimit", 50 );
	1372	+		m_VoteKickAllowed = CFG->GetInt( "bot_votekickallowed", 1 ) == 0 ? false : true;
	1373	+		m_VoteKickPercentage = CFG->GetInt( "bot_votekickpercentage", 100 );
	1374	+	
	1375	+		if( m_VoteKickPercentage > 100 )
	1376	+		{
	1377	+			m_VoteKickPercentage = 100;
	1378	+			CONSOLE_Print( "[GHOST] warning - bot_votekickpercentage is greater than 100, using 100 instead" );
	1379	+		}
	1380	+	
	1381	+		m_MOTDFile = CFG->GetString( "bot_motdfile", "motd.txt" );
	1382	+		m_GameLoadedFile = CFG->GetString( "bot_gameloadedfile", "gameloaded.txt" );
	1383	+		m_GameOverFile = CFG->GetString( "bot_gameoverfile", "gameover.txt" );
	1384	+		m_LocalAdminMessages = CFG->GetInt( "bot_localadminmessages", 1 ) == 0 ? false : true;
	1385	+		m_TCPNoDelay = CFG->GetInt( "tcp_nodelay", 0 ) == 0 ? false : true;
	1386	+		m_MatchMakingMethod = CFG->GetInt( "bot_matchmakingmethod", 1 );
	1387	+		m_MapGameType = CFG->GetUInt( "bot_mapgametype", 0 );
	1388	+	}
	1389	+	
	1390	+	void CGHost :: ExtractScripts( )
	1391	+	{
	1392	+		string PatchMPQFileName = m_Warcraft3Path + "War3Patch.mpq";
	1393	+		HANDLE PatchMPQ;
	1394	+	
	1395	+		if( SFileOpenArchive( PatchMPQFileName.c_str( ), 0, MPQ_OPEN_FORCE_MPQ_V1, &PatchMPQ ) )
	1396	+		{
	1397	+			CONSOLE_Print( "[GHOST] loading MPQ file [" + PatchMPQFileName + "]" );
	1398	+			HANDLE SubFile;
	1399	+	
	1400	+			// common.j
	1401	+	
	1402	+			if( SFileOpenFileEx( PatchMPQ, "Scripts\\common.j", 0, &SubFile ) )
	1403	+			{
	1404	+				uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
	1405	+	
	1406	+				if( FileLength > 0 && FileLength != 0xFFFFFFFF )
	1407	+				{
	1408	+					char *SubFileData = new char[FileLength];
	1409	+					DWORD BytesRead = 0;
	1410	+	
	1411	+					if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
	1412	+					{
	1413	+						CONSOLE_Print( "[GHOST] extracting Scripts\\common.j from MPQ file to [" + m_MapCFGPath + "common.j]" );
	1414	+						UTIL_FileWrite( m_MapCFGPath + "common.j", (unsigned char *)SubFileData, BytesRead );
	1415	+					}
	1416	+					else
	1417	+						CONSOLE_Print( "[GHOST] warning - unable to extract Scripts\\common.j from MPQ file" );
	1418	+	
	1419	+					delete [] SubFileData;
	1420	+				}
	1421	+	
	1422	+				SFileCloseFile( SubFile );
	1423	+			}
	1424	+			else
	1425	+				CONSOLE_Print( "[GHOST] couldn't find Scripts\\common.j in MPQ file" );
	1426	+	
	1427	+			// blizzard.j
	1428	+	
	1429	+			if( SFileOpenFileEx( PatchMPQ, "Scripts\\blizzard.j", 0, &SubFile ) )
	1430	+			{
	1431	+				uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
	1432	+	
	1433	+				if( FileLength > 0 && FileLength != 0xFFFFFFFF )
	1434	+				{
	1435	+					char *SubFileData = new char[FileLength];
	1436	+					DWORD BytesRead = 0;
	1437	+	
	1438	+					if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
	1439	+					{
	1440	+						CONSOLE_Print( "[GHOST] extracting Scripts\\blizzard.j from MPQ file to [" + m_MapCFGPath + "blizzard.j]" );
	1441	+						UTIL_FileWrite( m_MapCFGPath + "blizzard.j", (unsigned char *)SubFileData, BytesRead );
	1442	+					}
	1443	+					else
	1444	+						CONSOLE_Print( "[GHOST] warning - unable to extract Scripts\\blizzard.j from MPQ file" );
	1445	+	
	1446	+					delete [] SubFileData;
	1447	+				}
	1448	+	
	1449	+				SFileCloseFile( SubFile );
	1450	+			}
	1451	+			else
	1452	+				CONSOLE_Print( "[GHOST] couldn't find Scripts\\blizzard.j in MPQ file" );
	1453	+	
	1454	+			SFileCloseArchive( PatchMPQ );
	1455	+		}
	1456	+		else
	1457	+			CONSOLE_Print( "[GHOST] warning - unable to load MPQ file [" + PatchMPQFileName + "] - error code " + UTIL_ToString( GetLastError( ) ) );
	1458	+	}
	1459	+	
	1460	+	void CGHost :: LoadIPToCountryData( )
	1461	+	{
	1462	+		ifstream in;
	1463	+		in.open( "ip-to-country.csv" );
	1464	+	
	1465	+		if( in.fail( ) )
	1466	+			CONSOLE_Print( "[GHOST] warning - unable to read file [ip-to-country.csv], iptocountry data not loaded" );
	1467	+		else
	1468	+		{
	1469	+			CONSOLE_Print( "[GHOST] started loading [ip-to-country.csv]" );
	1470	+	
	1471	+			// the begin and commit statements are optimizations
	1472	+			// we're about to insert ~4 MB of data into the database so if we allow the database to treat each insert as a transaction it will take a LONG time
	1473	+			// todotodo: handle begin/commit failures a bit more gracefully
	1474	+	
	1475	+			if( !m_DBLocal->Begin( ) )
	1476	+				CONSOLE_Print( "[GHOST] warning - failed to begin local database transaction, iptocountry data not loaded" );
	1477	+			else
	1478	+			{
	1479	+				unsigned char Percent = 0;
	1480	+				string Line;
	1481	+				string IP1;
	1482	+				string IP2;
	1483	+				string Country;
	1484	+				CSVParser parser;
	1485	+	
	1486	+				// get length of file for the progress meter
	1487	+	
	1488	+				in.seekg( 0, ios :: end );
	1489	+				uint32_t FileLength = in.tellg( );
	1490	+				in.seekg( 0, ios :: beg );
	1491	+	
	1492	+				while( !in.eof( ) )
	1493	+				{
	1494	+					getline( in, Line );
	1495	+	
	1496	+					if( Line.empty( ) )
	1497	+						continue;
	1498	+	
	1499	+					parser << Line;
	1500	+					parser >> IP1;
	1501	+					parser >> IP2;
	1502	+					parser >> Country;
	1503	+					m_DBLocal->FromAdd( UTIL_ToUInt32( IP1 ), UTIL_ToUInt32( IP2 ), Country );
	1504	+	
	1505	+					// it's probably going to take awhile to load the iptocountry data (~10 seconds on my 3.2 GHz P4 when using SQLite3)
	1506	+					// so let's print a progress meter just to keep the user from getting worried
	1507	+	
	1508	+					unsigned char NewPercent = (unsigned char)( (float)in.tellg( ) / FileLength * 100 );
	1509	+	
	1510	+					if( NewPercent != Percent && NewPercent % 10 == 0 )
	1511	+					{
	1512	+						Percent = NewPercent;
	1513	+						CONSOLE_Print( "[GHOST] iptocountry data: " + UTIL_ToString( Percent ) + "% loaded" );
	1514	+					}
	1515	+				}
	1516	+	
	1517	+				if( !m_DBLocal->Commit( ) )
	1518	+					CONSOLE_Print( "[GHOST] warning - failed to commit local database transaction, iptocountry data not loaded" );
	1519	+				else
	1520	+					CONSOLE_Print( "[GHOST] finished loading [ip-to-country.csv]" );
	1521	+			}
	1522	+	
	1523	+			in.close( );
	1524	+		}
	1525	+	}
	1526	+	
	1527	+	void CGHost :: CreateGame( CMap *map, unsigned char gameState, bool saveGame, string gameName, string ownerName, string creatorName, string creatorServer, bool whisper )
	1528	+	{
	1529	+		if( !m_Enabled )
	1530	+		{
	1531	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1532	+			{
	1533	+				if( (*i)->GetServer( ) == creatorServer )
	1534	+					(*i)->QueueChatCommand( m_Language->UnableToCreateGameDisabled( gameName ), creatorName, whisper );
	1535	+			}
	1536	+	
	1537	+			if( m_AdminGame )
	1538	+				m_AdminGame->SendAllChat( m_Language->UnableToCreateGameDisabled( gameName ) );
	1539	+	
	1540	+			return;
	1541	+		}
	1542	+	
	1543	+		if( gameName.size( ) > 31 )
	1544	+		{
	1545	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1546	+			{
	1547	+				if( (*i)->GetServer( ) == creatorServer )
	1548	+					(*i)->QueueChatCommand( m_Language->UnableToCreateGameNameTooLong( gameName ), creatorName, whisper );
	1549	+			}
	1550	+	
	1551	+			if( m_AdminGame )
	1552	+				m_AdminGame->SendAllChat( m_Language->UnableToCreateGameNameTooLong( gameName ) );
	1553	+	
	1554	+			return;
	1555	+		}
	1556	+	
	1557	+		if( !map->GetValid( ) )
	1558	+		{
	1559	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1560	+			{
	1561	+				if( (*i)->GetServer( ) == creatorServer )
	1562	+					(*i)->QueueChatCommand( m_Language->UnableToCreateGameInvalidMap( gameName ), creatorName, whisper );
	1563	+			}
	1564	+	
	1565	+			if( m_AdminGame )
	1566	+				m_AdminGame->SendAllChat( m_Language->UnableToCreateGameInvalidMap( gameName ) );
	1567	+	
	1568	+			return;
	1569	+		}
	1570	+	
	1571	+		if( saveGame )
	1572	+		{
	1573	+			if( !m_SaveGame->GetValid( ) )
	1574	+			{
	1575	+	                        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1576	+				{
	1577	+					if( (*i)->GetServer( ) == creatorServer )
	1578	+						(*i)->QueueChatCommand( m_Language->UnableToCreateGameInvalidSaveGame( gameName ), creatorName, whisper );
	1579	+				}
	1580	+	
	1581	+				if( m_AdminGame )
	1582	+					m_AdminGame->SendAllChat( m_Language->UnableToCreateGameInvalidSaveGame( gameName ) );
	1583	+	
	1584	+				return;
	1585	+			}
	1586	+	
	1587	+			string MapPath1 = m_SaveGame->GetMapPath( );
	1588	+			string MapPath2 = map->GetMapPath( );
	1589	+			transform( MapPath1.begin( ), MapPath1.end( ), MapPath1.begin( ), (int(*)(int))tolower );
	1590	+			transform( MapPath2.begin( ), MapPath2.end( ), MapPath2.begin( ), (int(*)(int))tolower );
	1591	+	
	1592	+			if( MapPath1 != MapPath2 )
	1593	+			{
	1594	+				CONSOLE_Print( "[GHOST] path mismatch, saved game path is [" + MapPath1 + "] but map path is [" + MapPath2 + "]" );
	1595	+	
	1596	+	                        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1597	+				{
	1598	+					if( (*i)->GetServer( ) == creatorServer )
	1599	+						(*i)->QueueChatCommand( m_Language->UnableToCreateGameSaveGameMapMismatch( gameName ), creatorName, whisper );
	1600	+				}
	1601	+	
	1602	+				if( m_AdminGame )
	1603	+					m_AdminGame->SendAllChat( m_Language->UnableToCreateGameSaveGameMapMismatch( gameName ) );
	1604	+	
	1605	+				return;
	1606	+			}
	1607	+	
	1608	+			if( m_EnforcePlayers.empty( ) )
	1609	+			{
	1610	+	                        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1611	+				{
	1612	+					if( (*i)->GetServer( ) == creatorServer )
	1613	+						(*i)->QueueChatCommand( m_Language->UnableToCreateGameMustEnforceFirst( gameName ), creatorName, whisper );
	1614	+				}
	1615	+	
	1616	+				if( m_AdminGame )
	1617	+					m_AdminGame->SendAllChat( m_Language->UnableToCreateGameMustEnforceFirst( gameName ) );
	1618	+	
	1619	+				return;
	1620	+			}
	1621	+		}
	1622	+	
	1623	+		if( m_CurrentGame )
	1624	+		{
	1625	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1626	+			{
	1627	+				if( (*i)->GetServer( ) == creatorServer )
	1628	+					(*i)->QueueChatCommand( m_Language->UnableToCreateGameAnotherGameInLobby( gameName, m_CurrentGame->GetDescription( ) ), creatorName, whisper );
	1629	+			}
	1630	+	
	1631	+			if( m_AdminGame )
	1632	+				m_AdminGame->SendAllChat( m_Language->UnableToCreateGameAnotherGameInLobby( gameName, m_CurrentGame->GetDescription( ) ) );
	1633	+	
	1634	+			return;
	1635	+		}
	1636	+	
	1637	+		if( m_Games.size( ) >= m_MaxGames )
	1638	+		{
	1639	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1640	+			{
	1641	+				if( (*i)->GetServer( ) == creatorServer )
	1642	+					(*i)->QueueChatCommand( m_Language->UnableToCreateGameMaxGamesReached( gameName, UTIL_ToString( m_MaxGames ) ), creatorName, whisper );
	1643	+			}
	1644	+	
	1645	+			if( m_AdminGame )
	1646	+				m_AdminGame->SendAllChat( m_Language->UnableToCreateGameMaxGamesReached( gameName, UTIL_ToString( m_MaxGames ) ) );
	1647	+	
	1648	+			return;
	1649	+		}
	1650	+	
	1651	+		CONSOLE_Print( "[GHOST] creating game [" + gameName + "]" );
	1652	+	
	1653	+		if( saveGame )
	1654	+			m_CurrentGame = new CGame( this, map, m_SaveGame, m_HostPort, gameState, gameName, ownerName, creatorName, creatorServer );
	1655	+		else
	1656	+			m_CurrentGame = new CGame( this, map, NULL, m_HostPort, gameState, gameName, ownerName, creatorName, creatorServer );
	1657	+	
	1658	+		// todotodo: check if listening failed and report the error to the user
	1659	+	
	1660	+		if( m_SaveGame )
	1661	+		{
	1662	+			m_CurrentGame->SetEnforcePlayers( m_EnforcePlayers );
	1663	+			m_EnforcePlayers.clear( );
	1664	+		}
	1665	+	
	1666	+	        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1667	+		{
	1668	+			if( whisper && (*i)->GetServer( ) == creatorServer )
	1669	+			{
	1670	+				// note that we send this whisper only on the creator server
	1671	+	
	1672	+				if( gameState == GAME_PRIVATE )
	1673	+					(*i)->QueueChatCommand( m_Language->CreatingPrivateGame( gameName, ownerName ), creatorName, whisper );
	1674	+				else if( gameState == GAME_PUBLIC )
	1675	+					(*i)->QueueChatCommand( m_Language->CreatingPublicGame( gameName, ownerName ), creatorName, whisper );
	1676	+			}
	1677	+			else
	1678	+			{
	1679	+				// note that we send this chat message on all other bnet servers
	1680	+	
	1681	+				if( gameState == GAME_PRIVATE )
	1682	+					(*i)->QueueChatCommand( m_Language->CreatingPrivateGame( gameName, ownerName ) );
	1683	+				else if( gameState == GAME_PUBLIC )
	1684	+					(*i)->QueueChatCommand( m_Language->CreatingPublicGame( gameName, ownerName ) );
	1685	+			}
	1686	+	
	1687	+			if( saveGame )
	1688	+				(*i)->QueueGameCreate( gameState, gameName, string( ), map, m_SaveGame, m_CurrentGame->GetHostCounter( ) );
	1689	+			else
	1690	+				(*i)->QueueGameCreate( gameState, gameName, string( ), map, NULL, m_CurrentGame->GetHostCounter( ) );
	1691	+		}
	1692	+	
	1693	+		if( m_AdminGame )
	1694	+		{
	1695	+			if( gameState == GAME_PRIVATE )
	1696	+				m_AdminGame->SendAllChat( m_Language->CreatingPrivateGame( gameName, ownerName ) );
	1697	+			else if( gameState == GAME_PUBLIC )
	1698	+				m_AdminGame->SendAllChat( m_Language->CreatingPublicGame( gameName, ownerName ) );
	1699	+		}
	1700	+	
	1701	+		// if we're creating a private game we don't need to send any game refresh messages so we can rejoin the chat immediately
	1702	+		// unfortunately this doesn't work on PVPGN servers because they consider an enterchat message to be a gameuncreate message when in a game
	1703	+		// so don't rejoin the chat if we're using PVPGN
	1704	+	
	1705	+		if( gameState == GAME_PRIVATE )
	1706	+		{
	1707	+	                for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1708	+			{
	1709	+				if( (*i)->GetPasswordHashType( ) != "pvpgn" )
	1710	+					(*i)->QueueEnterChat( );
	1711	+			}
	1712	+		}
	1713	+	
	1714	+		// hold friends and/or clan members
	1715	+	
	1716	+	        for( vector<CBNET *> :: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); ++i )
	1717	+		{
	1718	+			if( (*i)->GetHoldFriends( ) )
	1719	+				(*i)->HoldFriends( m_CurrentGame );
	1720	+	
	1721	+			if( (*i)->GetHoldClan( ) )
	1722	+				(*i)->HoldClan( m_CurrentGame );
	1723	+		}
	1724	+	}
